#ifndef _PartonList_H_
#define _PartonList_H_

//
// PartonList.h
//
// Created by TB on 11/23/11.
// Copyright 2011 BNL. All rights reserved.
//

#include <map>
#include <vector>

#include <Rtypes.h>

#include <algorithm> // sort
#include <stdexcept> // out_of_range

#ifndef __CINT__ // CINT don't take kindly to them there BOOST headers...
#include <boost/foreach.hpp>
#endif // __CINT__

#ifdef USING_LHAPDF
#include <LHAPDF/LHAPDF.h>
#else
#include "tppmc/Enumerations.h" // For LHAPDF::Flavour enum
#endif

//#include "tppmc/PartonContent.h"
#include "tppmc/Particle.h"

namespace tppmc {
   
//   class pythpart;
   
   
   /**
    A collection of values for each LHAPDF::Flavour
    i.e. all quark flavours plus gluon and photon.
    */
   template<typename T>
   class PartonList {
      
   public:
      
      PartonList(T initial = T());
      
      virtual ~PartonList();
      
      /**
       Access the value for a flavour.
       Use the enum LHAPDF::Flavour to access the appropriate flavour.
       */
      virtual const T& operator[](int flavour) const;
      
      /**
       \overload
       */
      virtual T& operator[](int flavour);
      
      /**
       Functions the same as operator[].
       For compatibility with PyROOT, where the subscripting is not supported.
      */
      virtual const T& Get(int flavour) const;
      
      /**
       Returns true if the input flavour is contained in the collection.
       */
      static bool Supported(int);
      
      typedef std::vector<int> FlavourList;
      
      /**
       Returns a sorted collection of all supported flavours.
       */
      static const FlavourList& Flavours();
      
   protected:
      
      std::map<char, T> mPartons;
      
      static FlavourList smFlavours;
      
      //      ClassDef(tppmc::PartonList, 1)
   };
   
   
   //
   // class PartonList
   //
   
   
   template<typename T>
   typename PartonList<T>::FlavourList PartonList<T>::smFlavours;
   
   template<typename T>
   PartonList<T>::PartonList(T initial) {
#ifndef __CINT__
      BOOST_FOREACH(int flavour, Flavours()) {
         mPartons.insert(std::make_pair(flavour, initial));
      } // BOOST_FOREACH
#endif
   }
   
   
   template<typename T>
   PartonList<T>::~PartonList() {
   }
   
   
   template<typename T>
   const T& PartonList<T>::operator[](int index) const {
      if(not Supported(index)) {
         throw std::out_of_range("PartonList<T>::operator[]: invalid parton");
      } // if
      
      return mPartons.find(index)->second;
   }
   
   
   template<typename T>
   T& PartonList<T>::operator[](int index) {
      if(not Supported(index)) {
         throw std::out_of_range("PartonList<T>::operator[]: invalid parton");
      } // if
      
      return mPartons[index];
   }
   
   
   template<typename T>
   const T& PartonList<T>::Get(int index) const {
      if(not Supported(index)) {
         throw std::out_of_range("PartonList<T>::Get(): invalid parton");
      } // if
      return mPartons.find(index)->second;
   }
   
   
   template<typename T>
   bool PartonList<T>::Supported(int index) {
      return not(index < Flavours().front() or index > Flavours().back());
   }
   
   
   template<typename T>
   const typename PartonList<T>::FlavourList& PartonList<T>::Flavours() {
      
      // Populate the list on the first call
      if(smFlavours.empty()) {
         FlavourList::value_type array[14] = {
            LHAPDF::TBAR, LHAPDF::BBAR, LHAPDF::CBAR, LHAPDF::SBAR,
            LHAPDF::UBAR, LHAPDF::DBAR, LHAPDF::GLUON,
            LHAPDF::DOWN, LHAPDF::UP, LHAPDF::STRANGE, LHAPDF::CHARM,
            LHAPDF::BOTTOM, LHAPDF::TOP, LHAPDF::PHOTON
         };
         smFlavours.assign(array, array + 14);
         std::sort(smFlavours.begin(), smFlavours.end());
      } // if
      
      return smFlavours;
   }
   
   
   typedef PartonList<double> PartonContent;
}

#endif
